home *** CD-ROM | disk | FTP | other *** search
- Subject: v20i025: Deliver, flexible email delivery system, Part03/04
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Chip Salzenberg <chip@ateng.com>
- Posting-number: Volume 20, Issue 25
- Archive-name: deliver2.0/part03
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: debug.c dest.c dfile.c lock.c main.c
- # Wrapped by network@ateng on Fri Jun 9 13:21:01 1989
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'debug.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'debug.c'\"
- else
- echo shar: Extracting \"'debug.c'\" \(1063 characters\)
- sed "s/^X//" >'debug.c' <<'END_OF_FILE'
- X/* $Header: debug.c,v 2.1 89/06/09 12:25:17 network Exp $
- X *
- X * Debugging output.
- X *
- X * $Log: debug.c,v $
- X * Revision 2.1 89/06/09 12:25:17 network
- X * Update RCS revisions.
- X *
- X * Revision 1.4 89/06/09 12:23:42 network
- X * Baseline for 2.0 release.
- X *
- X */
- X
- X#include "deliver.h"
- X
- X/*----------------------------------------------------------------------
- X * Print out a complete dump of all destinations
- X */
- X
- Xdumpdests(when)
- Xchar *when;
- X{
- X DEST *d;
- X
- X message("Destinations %s:\n", when);
- X for (d = first_dest(); d; d = next_dest(d))
- X {
- X message("\t%s", d->d_name);
- X
- X switch (d->d_class)
- X {
- X case CL_USER:
- X /* it's understood */
- X break;
- X case CL_MBOX:
- X message(", mailbox='%s'", d->d_mailbox);
- X break;
- X case CL_UUCP:
- X message(" (UUCP)");
- X break;
- X }
- X message("; ");
- X switch (d->d_state)
- X {
- X case ST_WORKING:
- X message("Working");
- X break;
- X case ST_HOLD:
- X message("Hold");
- X break;
- X case ST_DONE:
- X message("Done");
- X break;
- X case ST_ERROR:
- X message("Error (%s)", derrmsg(d->d_error));
- X break;
- X }
- X message("\n");
- X }
- X}
- END_OF_FILE
- if test 1063 -ne `wc -c <'debug.c'`; then
- echo shar: \"'debug.c'\" unpacked with wrong size!
- fi
- # end of 'debug.c'
- fi
- if test -f 'dest.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'dest.c'\"
- else
- echo shar: Extracting \"'dest.c'\" \(2938 characters\)
- sed "s/^X//" >'dest.c' <<'END_OF_FILE'
- X/* $Header: dest.c,v 2.1 89/06/09 12:25:22 network Exp $
- X *
- X * Operations on the list of mail destinations.
- X *
- X * $Log: dest.c,v $
- X * Revision 2.1 89/06/09 12:25:22 network
- X * Update RCS revisions.
- X *
- X * Revision 1.5 89/06/09 12:23:45 network
- X * Baseline for 2.0 release.
- X *
- X */
- X
- X#include "deliver.h"
- X
- X/*
- X * Local data.
- X */
- X
- Xstatic DEST deadhead = { &deadhead, &deadhead };
- X#define HEADPTR (&deadhead)
- X
- X/*----------------------------------------------------------------------
- X * Add a new destination to the list (unless it already exists).
- X * Return pointer to DEST.
- X */
- X
- XDEST *
- Xdest(name, mailbox)
- Xchar *name;
- Xchar *mailbox;
- X{
- X DEST *d;
- X DCLASS class;
- X
- X if (strchr(name, '!'))
- X class = CL_UUCP;
- X else if (mailbox)
- X class = CL_MBOX;
- X else
- X class = CL_USER;
- X
- X for (d = HEADPTR->d_next; d != HEADPTR; d = d->d_next)
- X {
- X if (d->d_class != class)
- X continue;
- X
- X if (strcmp(d->d_name, name) != 0)
- X continue;
- X
- X /*
- X * If this destination has a named mailbox, then
- X * test it for equality as well.
- X */
- X
- X if (class == CL_MBOX
- X && strcmp(d->d_mailbox, mailbox) != 0)
- X continue;
- X
- X /*
- X * Like, gnarly, dude! It's already in the chain!
- X */
- X
- X return d;
- X }
- X
- X /*
- X * The given dest isn't in the list, so we have to add it.
- X */
- X
- X d = (DEST *) zalloc(sizeof(DEST));
- X d->d_class = class;
- X d->d_state = ST_WORKING;
- X d->d_name = copystr(name);
- X if (class == CL_MBOX)
- X d->d_mailbox = copystr(mailbox);
- X
- X /*
- X * Check address for validity.
- X */
- X
- X if (!valid_address(name))
- X dest_err(d, E_IVADDR);
- X else if (class != CL_UUCP && name_context(name) == NULL)
- X dest_err(d, E_NSUSER);
- X
- X /*
- X * Put new address at the end of of the chain.
- X * (This is important! Other code depends on it.)
- X */
- X
- X d->d_prev = HEADPTR->d_prev;
- X d->d_next = HEADPTR;
- X d->d_prev->d_next = d;
- X d->d_next->d_prev = d;
- X
- X return d;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Return pointer to first DEST in the list.
- X */
- X
- XDEST *
- Xfirst_dest()
- X{
- X if (HEADPTR->d_next != HEADPTR)
- X return HEADPTR->d_next;
- X
- X return NULL;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Return pointer to next DEST in the list, or NULL.
- X */
- X
- XDEST *
- Xnext_dest(d)
- XDEST *d;
- X{
- X if (d && (d = d->d_next) != HEADPTR)
- X return d;
- X
- X return NULL;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Return an error message given a DERROR.
- X */
- X
- Xchar *
- Xderrmsg(e)
- XDERROR e;
- X{
- X static char unknown_buf[40];
- X
- X switch (e)
- X {
- X case E_IVADDR:
- X return "Invalid address string";
- X case E_NSUSER:
- X return "No such user";
- X case E_NSHOST:
- X return "No such host (UUCP addresses)";
- X case E_CTPERM:
- X return "No permissions for that context";
- X case E_CTLOST:
- X return "Context lost (should never happen)";
- X case E_MBOX:
- X return "Can't write to mailbox";
- X case E_UUX:
- X return "Can't pipe to uux";
- X }
- X
- X (void) sprintf(unknown_buf, "Unknown error %d", e);
- X return unknown_buf;
- X}
- END_OF_FILE
- if test 2938 -ne `wc -c <'dest.c'`; then
- echo shar: \"'dest.c'\" unpacked with wrong size!
- fi
- # end of 'dest.c'
- fi
- if test -f 'dfile.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'dfile.c'\"
- else
- echo shar: Extracting \"'dfile.c'\" \(8068 characters\)
- sed "s/^X//" >'dfile.c' <<'END_OF_FILE'
- X/* $Header: dfile.c,v 2.1 89/06/09 12:25:24 network Exp $
- X *
- X * Filter destination(s) through delivery file(s).
- X *
- X * $Log: dfile.c,v $
- X * Revision 2.1 89/06/09 12:25:24 network
- X * Update RCS revisions.
- X *
- X * Revision 1.10 89/06/09 12:23:49 network
- X * Baseline for 2.0 release.
- X *
- X */
- X
- X#include "deliver.h"
- X#include <sys/stat.h>
- X
- X/*----------------------------------------------------------------------
- X * Filter all valid destinations through the global delivery file.
- X */
- X
- Xsys_dfile(dac, dav)
- Xint dac;
- Xchar **dav;
- X{
- X char **fav;
- X int fac, a;
- X struct stat st;
- X
- X /*
- X * If there is no global delivery file, forget it.
- X */
- X
- X if (stat(sys_deliver, &st) == -1)
- X {
- X if (verbose)
- X message("no system delivery file\n");
- X return -1;
- X }
- X
- X /*
- X * If we've been asked not to run delivery files, forget it.
- X */
- X
- X if (!rundfiles)
- X {
- X if (verbose)
- X message("system delivery file disabled\n");
- X return -1;
- X }
- X
- X /*
- X * Collect the arguments for the delivery file.
- X */
- X
- X fav = (char **) zalloc((dac + 3) * sizeof(char **));
- X fav[0] = shell;
- X fav[1] = sys_deliver;
- X fac = 2;
- X
- X for (a = 0; a < dac; ++a)
- X {
- X char *addr;
- X
- X addr = dav[a];
- X if (valid_address(addr))
- X {
- X /* Let the delivery file handle valid addresses. */
- X
- X fav[fac++] = addr;
- X }
- X else
- X {
- X /* Note invalid address(es); report them later. */
- X
- X (void) dest(addr, (char *) NULL);
- X }
- X }
- X
- X fav[fac] = NULL;
- X
- X /*
- X * If there were any good names found, let loose the delivery
- X * file. Note the meaning of "good" is "well-formed", not "valid".
- X * Thus the system delivery file has control over the handling of
- X * all local deliveries, not just those to valid users.
- X */
- X
- X if (fac > 2)
- X (void) do_dfile(eff_ct, fav, (DEST *)NULL);
- X
- X free((char *) fav);
- X
- X return 0;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Filter some undelivered destinations through the post-user
- X * delivery file.
- X */
- X
- Xpost_dfile()
- X{
- X DEST *d;
- X char **fav;
- X int num_dests, fac;
- X struct stat st;
- X
- X /*
- X * If there is no post-user delivery file, forget it.
- X */
- X
- X if (stat(post_deliver, &st) == -1)
- X {
- X if (verbose)
- X message("no post-user delivery file\n");
- X return -1;
- X }
- X
- X /*
- X * If we've been asked not to run delivery files, forget it.
- X */
- X
- X if (!rundfiles)
- X {
- X if (verbose)
- X message("post-user delivery file disabled\n");
- X return -1;
- X }
- X
- X /*
- X * Generate the delivery file argument list.
- X */
- X
- X num_dests = 0;
- X for (d = first_dest(); d; d = next_dest(d))
- X ++num_dests;
- X
- X fav = (char **) zalloc((num_dests + 3) * sizeof(char **));
- X fav[0] = shell;
- X fav[1] = post_deliver;
- X fac = 2;
- X
- X for (d = first_dest(); d; d = next_dest(d))
- X {
- X if ((d->d_class == CL_USER || d->d_class == CL_UUCP)
- X && (d->d_state == ST_WORKING
- X || (d->d_state == ST_ERROR && d->d_error == E_NSUSER)))
- X {
- X fav[fac++] = d->d_name;
- X d->d_state = ST_HOLD;
- X }
- X }
- X
- X fav[fac] = NULL;
- X
- X if (fac > 2)
- X (void) do_dfile(eff_ct, fav, (DEST *)NULL);
- X
- X free((char *) fav);
- X
- X return 0;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Filter all user destinations through their local delivery files.
- X */
- X
- Xuser_dfiles()
- X{
- X DEST *d;
- X int nfound;
- X
- X /*
- X * If we've been asked not to run delivery files, forget it.
- X */
- X
- X if (!rundfiles)
- X {
- X if (verbose)
- X message("user delivery files disabled\n");
- X
- X return -1;
- X }
- X
- X
- X /*
- X * Continue to loop through all addresses until no destination
- X * that needs expanding can be found.
- X */
- X
- X do {
- X nfound = 0;
- X for (d = first_dest(); d; d = next_dest(d))
- X {
- X if (d->d_class == CL_USER
- X && d->d_state == ST_WORKING
- X && !d->d_dfdone)
- X {
- X one_dfile(d);
- X d->d_dfdone = TRUE;
- X }
- X }
- X } while (nfound > 0);
- X
- X return 0;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Run the delivery file (if any) for the specified destination.
- X */
- X
- Xone_dfile(d)
- XDEST *d;
- X{
- X CONTEXT *ct;
- X char *fav[4];
- X char udel_path[100];
- X struct stat st;
- X
- X if ((ct = name_context(d->d_name)) == NULL)
- X {
- X dest_err(d, E_CTLOST);
- X return;
- X }
- X
- X /*
- X * If user's home directory is missing, forget it.
- X * If user's home directory is writable to the world,
- X * executing the delivery file would allow a security breach!
- X * Thanks to Jon Zeeff for this hint...
- X */
- X
- X if (stat(ct->ct_home, &st) == -1
- X || (st.st_mode & S_IFMT) != S_IFDIR)
- X {
- X if (verbose)
- X message("user %s: home directory %s is missing!\n",
- X ct->ct_name, ct->ct_home);
- X return;
- X }
- X
- X if (st.st_mode & 02)
- X {
- X if (verbose)
- X message("user %s: home directory is writable to the world!\n",
- X ct->ct_name);
- X return;
- X }
- X
- X /*
- X * If there is no delivery file to execute, just return.
- X */
- X
- X (void) sprintf(udel_path, "%s/%s", ct->ct_home, user_deliver);
- X if (stat(udel_path, &st) == -1)
- X {
- X if (verbose)
- X message("%s has no delivery file\n", d->d_name);
- X return;
- X }
- X
- X /*
- X * Time to run the file!
- X * We put this dest on hold, so that it will be ignored unless
- X * the delivery file names it.
- X */
- X
- X d->d_state = ST_HOLD;
- X
- X fav[0] = shell;
- X fav[1] = udel_path;
- X fav[2] = d->d_name;
- X fav[3] = NULL;
- X (void) do_dfile(ct, fav, d);
- X}
- X
- X/*----------------------------------------------------------------------
- X * Process a delivery file.
- X */
- X
- Xint
- Xdo_dfile(ct, av, d)
- XCONTEXT *ct;
- Xchar **av;
- XDEST *d;
- X{
- X FILE *fp;
- X char *name, *mailbox;
- X
- X if (!ct)
- X return -1;
- X
- X if (! ok_context(ct))
- X {
- X if (d)
- X dest_err(d, E_CTPERM);
- X else
- X message("No permissions to run as %s\n", ct->ct_name);
- X
- X return -1;
- X }
- X
- X /* Copy the temp files again */
- X
- X if (copy_again() < 0)
- X return -1;
- X
- X /* Allow the given user to own and read the copies */
- X
- X if (give_temps(ct) < 0)
- X return -1;
- X
- X /* Here we go! */
- X
- X if (verbose)
- X message("Processing delivery file as %s\n", ct->ct_name);
- X
- X if ((fp = ct_popenv(ct, shell, av, "r")) == NULL)
- X {
- X error("can't execute delivery file as %s\n", ct->ct_name);
- X return -1;
- X }
- X
- X /*
- X * Read the standard output of the delivery file.
- X */
- X
- X while (dfile_gets(fp, &name, &mailbox) >= 0)
- X {
- X DEST *nd;
- X
- X nd = dest(name, mailbox);
- X if (nd->d_state == ST_HOLD)
- X nd->d_state = ST_WORKING;
- X
- X /*
- X * If the delivery file specified a mailbox, verify
- X * that the user whose delivery file is running has
- X * permissions for the requested context.
- X */
- X
- X if ((nd->d_state == ST_WORKING) && (mailbox != NULL))
- X {
- X CONTEXT *nct;
- X
- X if ((nct = name_context(name)) == NULL)
- X dest_err(nd, E_CTLOST);
- X else if (! ok_context(nct))
- X dest_err(nd, E_CTPERM);
- X }
- X }
- X
- X return ct_pclose(fp);
- X}
- X
- X/*----------------------------------------------------------------------
- X * Get and parse a single delivery file output line.
- X */
- X
- Xint
- Xdfile_gets(fp, namep, mailboxp)
- XFILE *fp;
- Xchar **namep;
- Xchar **mailboxp;
- X{
- X char *p, *q;
- X static char buf[BUFSIZ];
- X
- X if (fgets(buf, GETSIZE(buf), fp) == NULL)
- X return -1;
- X
- X if ((p = strchr(buf, '\n')) != NULL)
- X *p = 0;
- X else
- X {
- X int c;
- X
- X while ((c = fgetc(fp)) != '\n' && c != EOF)
- X ; /* keep reading */
- X
- X error("invalid line from delivery file: '%s'\n", buf);
- X return -1;
- X }
- X
- X /* Strip out all whitespace and eliminate duplicated slashes */
- X
- X p = q = buf;
- X while (*p)
- X {
- X if (isspace(*p))
- X ++p;
- X else if ((*q++ = *p++) == '/')
- X {
- X while (*p == '/')
- X ++p;
- X }
- X }
- X *q = 0;
- X
- X /* Debugging message: display input line */
- X
- X if (verbose)
- X message("\t'%s'\n", buf);
- X
- X if ((p = strchr(buf, ':')) != NULL)
- X {
- X *p++ = 0;
- X if ((q = strchr(p, ':')) != NULL)
- X *q = 0;
- X }
- X
- X *namep = buf;
- X *mailboxp = p;
- X return 0;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Make the temp files readable in the given context.
- X * This is needed because the temps are readable by owner only.
- X */
- X
- Xint
- Xgive_temps(ct)
- XCONTEXT *ct;
- X{
- X int err, t;
- X
- X if (!ct)
- X return -1;
- X
- X err = 0;
- X for (t = T_HDRCOPY; t <= T_BODYCOPY; ++t)
- X {
- X if (chmod(tfile[t], 0600) == -1)
- X {
- X syserr("can't chmod %s", tfile[t]);
- X ++err;
- X }
- X if (chown(tfile[t], ct->ct_uid, ct->ct_gid) == -1)
- X {
- X syserr("can't chown %s to %d/%d",
- X tfile[t], ct->ct_uid, ct->ct_gid);
- X ++err;
- X }
- X }
- X
- X return err ? -1 : 0;
- X}
- END_OF_FILE
- if test 8068 -ne `wc -c <'dfile.c'`; then
- echo shar: \"'dfile.c'\" unpacked with wrong size!
- fi
- # end of 'dfile.c'
- fi
- if test -f 'lock.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'lock.c'\"
- else
- echo shar: Extracting \"'lock.c'\" \(7155 characters\)
- sed "s/^X//" >'lock.c' <<'END_OF_FILE'
- X/* $Header: lock.c,v 2.1 89/06/09 12:25:30 network Exp $
- X *
- X * Mailbox locking.
- X * Local hacks for mailbox access should be grafted here.
- X *
- X * $Log: lock.c,v $
- X * Revision 2.1 89/06/09 12:25:30 network
- X * Update RCS revisions.
- X *
- X * Revision 1.6 89/06/09 12:23:52 network
- X * Baseline for 2.0 release.
- X *
- X */
- X
- X#include "deliver.h"
- X
- X/*
- X * Validate the locking configuration.
- X */
- X
- X#if (defined(ML_FCNTL) + defined(ML_LOCKF) + defined(ML_LOCKING)) > 1
- X lose! "Only one of ML_FCNTL, ML_LOCKF and ML_LOCKING may be defined.";
- X#endif
- X
- X/*
- X * Support for the lockf() system call.
- X */
- X
- X#ifdef ML_LOCKF
- X#include <unistd.h>
- X#define SIMPLE_LOCK "lockf"
- X#define LOCKFD(fd, size) lockf(fd, F_LOCK, size)
- X#define UNLOCKFD(fd, size) lockf(fd, F_ULOCK, size)
- X#endif /* ML_LOCKF */
- X
- X/*
- X * Setup for the locking() system call.
- X */
- X
- X#ifdef ML_LOCKING
- X#include <sys/locking.h>
- X#define SIMPLE_LOCK "locking"
- X#define LOCKFD(fd, size) locking(fd, LK_LOCK, size)
- X#define UNLOCKFD(fd, size) locking(fd, LK_UNLOCK, size)
- X#endif
- X
- X/*
- X * Local functions.
- X */
- X
- X#ifdef ML_DOTLOCK
- Xstatic char *dotlock_name();
- X#endif
- X#ifdef ML_DOTMLK
- Xstatic char *dotmlk_name();
- X#endif
- X
- X/*----------------------------------------------------------------------
- X * Lock a mailbox by name.
- X *
- X * This code looks quite hairy with all the ifdefs. In fact, the only
- X * somewhat strange thing here is that neither, either, or both of
- X * ML_DOTLOCK and ML_DOTMLK may be defined, and we have to allow for it.
- X */
- X
- Xint
- Xname_lock(name)
- Xchar *name;
- X{
- X#ifdef ML_DOTLOCK
- X char *dotlock;
- X#endif
- X#ifdef ML_DOTMLK
- X char *dotmlk;
- X#endif
- X
- X#ifdef ML_DOTLOCK
- X if ((dotlock = dotlock_name(name)) == NULL
- X || create_lockfile(dotlock) < 0)
- X return -1;
- X#endif /* ML_DOTLOCK */
- X
- X#ifdef ML_DOTMLK
- X if ((dotmlk = dotmlk_name(name)) == NULL
- X || create_lockfile(dotmlk) < 0)
- X {
- X#ifdef ML_DOTLOCK
- X (void) remove_lockfile(dotlock); /* don't leave me hanging */
- X#endif
- X return -1;
- X }
- X#endif /* ML_DOTMLK */
- X
- X return 0;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Unlock a mailbox by name.
- X */
- X
- Xint
- Xname_unlock(name)
- Xchar *name;
- X{
- X int ret = 0;
- X
- X#ifdef ML_DOTLOCK
- X char *dotlock;
- X#endif
- X#ifdef ML_DOTMLK
- X char *dotmlk;
- X#endif
- X
- X#ifdef ML_DOTLOCK
- X if ((dotlock = dotlock_name(name)) == NULL
- X || remove_lockfile(dotlock) < 0)
- X ret = -1;
- X#endif /* ML_DOTLOCK */
- X
- X#ifdef ML_DOTMLK
- X if ((dotmlk = dotmlk_name(name)) == NULL
- X || remove_lockfile(dotmlk) < 0)
- X ret = -1;
- X#endif /* ML_DOTMLK */
- X
- X return ret;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Lock a file descriptor.
- X */
- X
- Xint
- Xfd_lock(fd)
- Xint fd;
- X{
- X#ifdef ML_FCNTL
- X struct flock fl;
- X
- X fl.l_type = F_WRLCK;
- X fl.l_whence = 0;
- X fl.l_start = 0L;
- X fl.l_len = 0L;
- X
- X if (fcntl(fd, F_SETLKW, &fl) == -1)
- X {
- X syserr("can't lock with fcntl()");
- X return -1;
- X }
- X
- X if (verbose)
- X message("locked mailbox with fcntl()\n");
- X#endif /* ML_FCNTL */
- X
- X#ifdef SIMPLE_LOCK
- X long pos;
- X
- X if ((pos = lseek(fd, 0L, 0)) == -1)
- X {
- X syserr("can't seek in mailbox");
- X return -1;
- X }
- X if (LOCKFD(fd, 0L) == -1)
- X {
- X syserr("can't lock with %s()", SIMPLE_LOCK);
- X return -1;
- X }
- X if (lseek(fd, pos, 0) == -1)
- X {
- X syserr("can't seek in mailbox");
- X return -1;
- X }
- X
- X if (verbose)
- X message("locked mailbox with %s()\n", SIMPLE_LOCK);
- X#endif /* SIMPLE_LOCK */
- X
- X /* Default: success */
- X return 0;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Unlock a file descriptor.
- X */
- X
- Xint
- Xfd_unlock(fd)
- Xint fd;
- X{
- X#ifdef ML_FCNTL
- X struct flock fl;
- X
- X fl.l_type = F_UNLCK;
- X fl.l_whence = 0;
- X fl.l_start = 0L;
- X fl.l_len = 0L;
- X
- X if (fcntl(fd, F_SETLKW, &fl) == -1)
- X {
- X syserr("can't unlock with fcntl()");
- X return -1;
- X }
- X
- X if (verbose)
- X message("unlocked mailbox with fcntl()\n");
- X#endif /* ML_FCNTL */
- X
- X#ifdef SIMPLE_LOCK
- X long pos;
- X
- X if ((pos = lseek(fd, 0L, 0)) == -1)
- X {
- X syserr("can't seek in mailbox");
- X return -1;
- X }
- X if (LOCKFD(fd, 0L) == -1)
- X {
- X syserr("can't unlock with %s()", SIMPLE_LOCK);
- X return -1;
- X }
- X if (lseek(fd, pos, 0) == -1)
- X {
- X syserr("can't seek in mailbox");
- X return -1;
- X }
- X
- X if (verbose)
- X message("unlocked mailbox with %s()\n", SIMPLE_LOCK);
- X#endif /* SIMPLE_LOCK */
- X
- X /* Default: success */
- X return 0;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Return the name of the appropriate ".lock" file for a mailbox.
- X */
- X
- X#ifdef ML_DOTLOCK
- X
- Xstatic char *
- Xdotlock_name(name)
- Xchar *name;
- X{
- X static char *lname = NULL;
- X static int lsize = 0;
- X char *p;
- X int n, i;
- X
- X n = strlen(name);
- X if (lsize < n + 8)
- X {
- X if (lname)
- X free(lname);
- X lsize = n + 32;
- X lname = zalloc(lsize);
- X }
- X
- X (void) strcpy(lname, name);
- X
- X /*
- X * We want as much of `basename.lock' as will fit in a string
- X * MAX_NAMESIZE long.
- X */
- X for (i = 0, p = basename(lname); (i < MAX_NAMESIZE - 5) && (*p); ++i)
- X ++p;
- X (void) strcpy(p, ".lock");
- X
- X return lname;
- X}
- X
- X#endif /* ML_DOTLOCK */
- X
- X/*----------------------------------------------------------------------
- X * Return the name of the appropriate ".mlk" file for a mailbox.
- X */
- X
- X#ifdef ML_DOTMLK
- X
- Xstatic char *
- Xdotmlk_name(name)
- Xchar *name;
- X{
- X static char lname[MAX_NAMESIZE + 16];
- X char *p, *d;
- X int i;
- X
- X /*
- X * To explain the below: If we ass_u_me that MAX_NAMESIZE is 14,
- X * then this code is like `printf(lname, "/tmp/%.10s.mlk", ...)'.
- X * In other words, we want as much of `basename.mlk' as will fit
- X * in a string MAX_NAMESIZE long.
- X */
- X d = lname;
- X for (p = "/tmp/"; *p; )
- X *d++ = *p++;
- X for (i = 0, p = basename(name); (i < MAX_NAMESIZE - 4) && (*p); ++i)
- X *d++ = *p++;
- X (void) strcpy(d, ".mlk");
- X
- X return lname;
- X}
- X
- X#endif /* ML_DOTMLK */
- X
- X/*----------------------------------------------------------------------
- X * Create a lockfile.
- X */
- X
- Xint
- Xcreate_lockfile(name)
- Xchar *name;
- X{
- X#ifndef O_CREAT
- X char *othername, *p;
- X#endif
- X int fd, tries;
- X
- X#ifndef O_CREAT
- X othername = zalloc(strlen(name) + 20); /* fudge (???) */
- X (void) strcpy(othername, name);
- X (void) sprintf(basename(othername), ".dl.%d", getpid());
- X if ((fd = creat(othername, 0)) == -1)
- X {
- X syserr("can't create %s", othername);
- X return -1;
- X }
- X (void) close(fd);
- X if (verbose)
- X message("created pre-lockfile %s\n", name);
- X#endif
- X
- X for (tries = 0; tries < 10; ++tries)
- X {
- X if (tries)
- X snooze(3);
- X
- X#ifdef O_CREAT
- X
- X if ((fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0)) >= 0)
- X {
- X (void) close(fd);
- X if (verbose)
- X message("created lockfile %s\n", name);
- X return 0;
- X }
- X
- X#else /* not O_CREAT */
- X
- X if (link(othername, name) == 0)
- X {
- X if (unlink(othername) == -1)
- X syserr("can't remove %s", othername);
- X free(othername);
- X if (verbose)
- X message("created lockfile %s\n", name);
- X return 0;
- X }
- X
- X#endif /* not O_CREAT */
- X
- X if (verbose && (tries == 0))
- X message("Waiting to create %s\n", name);
- X }
- X
- X syserr("can't create lockfile %s", name);
- X return -1;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Remove a lockfile.
- X */
- X
- Xint
- Xremove_lockfile(name)
- Xchar *name;
- X{
- X if (unlink(name) == -1)
- X {
- X syserr("can't remove lockfile %s", name);
- X return -1;
- X }
- X
- X if (verbose)
- X message("removed lockfile %s\n", name);
- X
- X return 0;
- X}
- END_OF_FILE
- if test 7155 -ne `wc -c <'lock.c'`; then
- echo shar: \"'lock.c'\" unpacked with wrong size!
- fi
- # end of 'lock.c'
- fi
- if test -f 'main.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'main.c'\"
- else
- echo shar: Extracting \"'main.c'\" \(12025 characters\)
- sed "s/^X//" >'main.c' <<'END_OF_FILE'
- X/* $Header: main.c,v 2.1 89/06/09 12:25:32 network Exp $
- X *
- X * A program to deliver local mail with some flexibility.
- X *
- X * $Log: main.c,v $
- X * Revision 2.1 89/06/09 12:25:32 network
- X * Update RCS revisions.
- X *
- X * Revision 1.13 89/06/09 12:23:53 network
- X * Baseline for 2.0 release.
- X *
- X */
- X
- X#include "deliver.h"
- X#include "patchlevel.h"
- X#include <signal.h>
- X
- X/*
- X * External data.
- X */
- X
- X/* Variables set by getopt() [blech] */
- X
- Xextern int optind, opterr;
- Xextern char *optarg;
- X
- X/*
- X * Local data
- X */
- X
- Xstatic char sys_dfl[] = SYS_DELIVER;
- Xstatic char post_dfl[] = POST_DELIVER;
- Xstatic char user_dfl[] = USER_DELIVER;
- X
- X/*
- X * Global data
- X */
- X
- Xint verbose = FALSE;
- Xint dryrun = FALSE;
- Xint rundfiles = TRUE;
- Xint printaddrs = FALSE;
- Xint leavetemps = FALSE;
- Xint boxdelivery = FALSE;
- X
- Xchar *progname = "deliver";
- Xchar version[32] = "2.0";
- Xchar *shell = SHELL;
- X
- Xchar *sys_deliver = sys_dfl;
- Xchar *post_deliver = post_dfl;
- Xchar *user_deliver = user_dfl;
- Xchar *sender = NULL;
- Xchar *hostname = NULL;
- X
- Xint eff_uid = -1;
- Xint eff_gid = -1;
- Xint real_uid = -1;
- Xint real_gid = -1;
- X
- XCONTEXT *eff_ct = NULL;
- XCONTEXT *real_ct = NULL;
- X
- Xint tty_input = FALSE;
- XSIGFLAG got_sig = FALSE;
- X
- Xint trust_user = FALSE;
- Xint trust_delfiles = FALSE;
- X
- Xchar *ttype[T_MAX] = { "header", "body", "header copy", "body copy" };
- Xchar *tfile[T_MAX] = { NULL, NULL, NULL, NULL };
- Xchar *tenv[T_MAX] = { NULL, NULL, ENV_HEADER, ENV_BODY };
- Xint tfd[T_MAX] = { -1, -1, -1, -1 };
- X
- X/*
- X * Local functions.
- X */
- X
- Xstatic SIGTYPE sighup(), sigint(), sigquit();
- X
- X/*----------------------------------------------------------------------
- X * The Program.
- X */
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X char *p;
- X int u, c, errcount, copy;
- X
- X /* Make sure that stdout and stderr are interleaved correctly */
- X
- X Linebuf(stdout);
- X Linebuf(stderr);
- X
- X /* Figure out the name used to invoke this program. */
- X
- X progname = basename(argv[0]);
- X
- X /* What version of the program is this? */
- X
- X (void) sprintf(version + strlen(version), ".%02d", PATCHLEVEL);
- X
- X /* Figure out the name of this host */
- X
- X if ((hostname = gethost()) == NULL)
- X {
- X hostname = "unknown";
- X error("unable to determine host name; using \"%s\"\n",
- X hostname);
- X }
- X
- X /* Find effective and real uids and gids. */
- X
- X eff_uid = geteuid();
- X eff_gid = getegid();
- X real_uid = getuid();
- X real_gid = getgid();
- X
- X if (eff_uid != real_uid && eff_uid != 0)
- X {
- X error("if setuid, must be setuid root\n");
- X leave(1);
- X }
- X
- X /* Process environment: handle recursive invocation */
- X
- X if ((p = getenv(ENV_DFLAGS)) != NULL)
- X {
- X while (*p)
- X {
- X switch (*p++)
- X {
- X case 'v':
- X verbose = TRUE;
- X break;
- X case 'd':
- X verbose = TRUE;
- X dryrun = TRUE;
- X break;
- X case 'A':
- X printaddrs = TRUE;
- X dryrun = TRUE;
- X break;
- X case 'n':
- X rundfiles = FALSE;
- X break;
- X case 't':
- X leavetemps = TRUE;
- X break;
- X }
- X }
- X }
- X
- X if ((p = getenv(ENV_SYSDEL)) != NULL && *p)
- X sys_deliver = p;
- X if ((p = getenv(ENV_POSTDEL)) != NULL && *p)
- X post_deliver = p;
- X if ((p = getenv(ENV_USERDEL)) != NULL && *p)
- X user_deliver = p;
- X if ((p = getenv(ENV_SENDER)) != NULL && *p)
- X sender = p;
- X if ((p = getenv(ENV_HOSTNAME)) != NULL && *p)
- X hostname = p;
- X
- X /* Parse command line arguments */
- X
- X while ((c = getopt(argc, argv, "vdAntbs:p:u:r:h:")) != EOF)
- X {
- X switch (c)
- X {
- X case 'v':
- X verbose = TRUE;
- X break;
- X case 'd':
- X verbose = TRUE;
- X dryrun = TRUE;
- X break;
- X case 'A':
- X printaddrs = TRUE;
- X dryrun = TRUE;
- X break;
- X case 'n':
- X rundfiles = FALSE;
- X break;
- X case 't':
- X leavetemps = TRUE;
- X break;
- X case 'b':
- X boxdelivery = TRUE;
- X break;
- X case 's':
- X if (*optarg)
- X sys_deliver = optarg;
- X break;
- X case 'p':
- X if (*optarg)
- X post_deliver = optarg;
- X break;
- X case 'u':
- X if (*optarg)
- X user_deliver = optarg;
- X break;
- X case 'r':
- X if (*optarg)
- X sender = optarg;
- X break;
- X case 'h':
- X if (*optarg)
- X hostname = optarg;
- X break;
- X case '?':
- X usage();
- X }
- X }
- X
- X /* If no destinations were given, forget it. */
- X
- X if (optind >= argc)
- X {
- X error("no recipients specified\n");
- X usage();
- X }
- X
- X /* Print a debugging message */
- X
- X if (verbose)
- X {
- X message("%s %s running on host %s\n",
- X progname, version, hostname);
- X }
- X
- X /* Do we trust our caller? */
- X
- X if (trusted_uid(real_uid))
- X trust_user = TRUE;
- X
- X /* Do we trust our delivery files? */
- X
- X if (strcmp(sys_dfl, sys_deliver) == 0
- X && strcmp(post_dfl, post_deliver) == 0
- X && strcmp(user_dfl, user_deliver) == 0)
- X trust_delfiles = TRUE;
- X
- X /* Renounce special privileges if something insecure was requested. */
- X
- X if (!trust_user && !trust_delfiles)
- X {
- X if (setgid(eff_gid = real_gid) == -1
- X || setuid(eff_uid = real_uid) == -1)
- X {
- X syserr("can't renounce setuid privileges");
- X leave(1);
- X }
- X }
- X
- X /* Get the contexts of our effective and real uids. */
- X
- X if ((eff_ct = uid_context(eff_uid)) == NULL)
- X error("invalid effective uid %d!?\n", eff_uid);
- X
- X if ((real_ct = uid_context(real_uid)) == NULL)
- X error("invalid real uid %d!?\n", real_uid);
- X
- X if (!eff_ct || !real_ct)
- X leave(1);
- X
- X if (verbose)
- X {
- X message("effective uid = %s (%d/%d); real uid = %s (%d/%d)\n",
- X eff_ct->ct_name, eff_ct->ct_uid, eff_ct->ct_gid,
- X real_ct->ct_name, real_ct->ct_uid, real_ct->ct_gid);
- X }
- X
- X /* Let's be sane about the file creation mask. */
- X
- X u = umask(0);
- X u &= ~0700; /* Let's not deprive ourselves of permissions. */
- X u |= 022; /* Let's be reasonably paranoid about writing. */
- X (void) umask(u);
- X
- X /*
- X * Where is the message coming from?
- X */
- X
- X if (isatty(0))
- X tty_input = TRUE;
- X
- X /*
- X * If we are not going to deliver, or if we are receiving the
- X * message from a tty, catch signals so we can remove temp files.
- X * Otherwise, ignore signals.
- X */
- X
- X if (dryrun || tty_input)
- X catch_sigs();
- X else
- X ignore_sigs();
- X
- X /*
- X * Create the temporary files and write the message to them.
- X */
- X
- X copy = copy_message();
- X
- X /*
- X * No more signals...
- X */
- X
- X ignore_sigs();
- X
- X /*
- X * ... but if we had already caught a signal,
- X * or if copy_msg() had a problem, leave.
- X */
- X
- X if ((copy < 0) || got_sig)
- X {
- X if (got_sig)
- X error("caught signal - exiting\n");
- X leave(1);
- X }
- X
- X /*
- X * Set up useful environment variables.
- X * Note that this must be done _after_ copy_message(),
- X * since that's where the temp files are created.
- X */
- X
- X setup_environ();
- X
- X /*
- X * Perhaps we should consider all arguments as mailbox names...
- X */
- X
- X if (boxdelivery)
- X {
- X int a;
- X
- X if (verbose)
- X message("mailbox delivery as %s\n", real_ct->ct_name);
- X
- X /*
- X * Consider all arguments as mailbox filenames.
- X */
- X
- X for (a = optind; a < argc; ++a)
- X (void) dest(real_ct->ct_name, argv[a]);
- X
- X if (verbose)
- X dumpdests("(should all be mailboxes)");
- X }
- X
- X /*
- X * They're not mailbox names, so they should be mail addresses.
- X */
- X
- X else
- X {
- X /* Run all destinations though the system delivery file. */
- X
- X if (sys_dfile(argc - optind, argv + optind) >= 0)
- X {
- X if (verbose)
- X dumpdests("after running system delivery file");
- X }
- X else
- X {
- X int a;
- X
- X /*
- X * System delivery file is missing or ignored.
- X * Use the argument list verbatim.
- X */
- X
- X for (a = optind; a < argc; ++a)
- X (void) dest(argv[a], (char *) NULL);
- X
- X if (verbose)
- X dumpdests("as taken from argument list");
- X }
- X
- X /*
- X * Run each user destination through his delivery file.
- X */
- X
- X if (user_dfiles() >= 0)
- X {
- X if (verbose)
- X dumpdests("after running user delivery files");
- X }
- X
- X /*
- X * Run each remaining destination though the post-user
- X * delivery file.
- X */
- X
- X if (post_dfile() >= 0)
- X {
- X if (verbose)
- X dumpdests("after running post-user delivery file");
- X }
- X }
- X
- X /*
- X * Drop mail in mailbox(es).
- X */
- X
- X mbox_deliver();
- X
- X if (verbose)
- X dumpdests("after delivery to all mailboxes");
- X
- X /*
- X * Send mail to UUCP address(es).
- X */
- X
- X uucp_deliver();
- X
- X if (verbose)
- X dumpdests("after delivery to UUCP addresses");
- X
- X /*
- X * Report any errors, and leave.
- X */
- X
- X errcount = report_errors();
- X
- X /*
- X * All done.
- X */
- X
- X leave(errcount ? 1 : 0);
- X /* NOTREACHED */
- X}
- X
- X/*----------------------------------------------------------------------
- X * Print a usage message and exit.
- X */
- X
- Xusage()
- X{
- X message("Usage: %s [-b][-A][-d][-v][-n][-t][-r from][-h host] args\n", progname);
- X message("-b All arguments are mailbox filenames.\n");
- X message(" (Default: arguments are user names.)\n");
- X message("-A Resolve addresses but do not deliver.\n");
- X message("-d Be verbose but do not deliver.\n");
- X message("-v Be verbose and deliver.\n");
- X message("-n Do not run any delivery files.\n");
- X message("-t Do not remote temp files before exiting.\n");
- X message("-s file Specify the system delivery filename.\n");
- X message("-p file Specify the post-user delivery filename.\n");
- X message("-u file Specify the user delivery filename.\n");
- X message("-r from Specify the address to appear in the \"From \" line.\n");
- X message("-h host Specify the host name.\n");
- X message("args Either user addresses or mailboxes (-b).\n");
- X leave(1);
- X}
- X
- X/*----------------------------------------------------------------------
- X * Clean up and exit.
- X */
- X
- Xleave(code)
- Xint code;
- X{
- X if (! leavetemps)
- X {
- X int t;
- X
- X for (t = 0; t < T_MAX; ++t)
- X {
- X if (tfd[t] != -1)
- X (void) close(tfd[t]);
- X if (tfile[t] && unlink(tfile[t]) == -1)
- X syserr("can't unlink %s", tfile[t]);
- X }
- X }
- X
- X exit(code);
- X}
- X
- X/*----------------------------------------------------------------------
- X * Catch signals.
- X */
- X
- Xcatch_sigs()
- X{
- X if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
- X (void) signal(SIGHUP, sighup);
- X if (signal(SIGINT, SIG_IGN) != SIG_IGN)
- X (void) signal(SIGINT, sigint);
- X if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
- X (void) signal(SIGQUIT, sigquit);
- X}
- X
- X/*----------------------------------------------------------------------
- X * Ignore signals.
- X */
- X
- Xignore_sigs()
- X{
- X (void) signal(SIGHUP, SIG_IGN);
- X (void) signal(SIGINT, SIG_IGN);
- X (void) signal(SIGQUIT, SIG_IGN);
- X}
- X
- Xstatic SIGTYPE
- Xsighup()
- X{
- X (void) signal(SIGHUP, sighup);
- X got_sig = TRUE;
- X}
- X
- Xstatic SIGTYPE
- Xsigint()
- X{
- X (void) signal(SIGINT, sigint);
- X got_sig = TRUE;
- X}
- X
- Xstatic SIGTYPE
- Xsigquit()
- X{
- X (void) signal(SIGQUIT, sigquit);
- X got_sig = TRUE;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Report any errors to stderr.
- X * Return an error count.
- X */
- X
- Xint
- Xreport_errors()
- X{
- X DEST *d;
- X int count = 0;
- X
- X for (d = first_dest(); d; d = next_dest(d))
- X {
- X if (d->d_state != ST_ERROR)
- X continue;
- X
- X if (++count == 1)
- X {
- X error(
- X "delivery to the following address(es) failed on host %s\n",
- X hostname);
- X }
- X
- X message("\t\"%s\"", d->d_name);
- X if (d->d_class == CL_MBOX)
- X message(", mailbox \"%s\"", d->d_mailbox);
- X message(": %s\n", derrmsg(d->d_error));
- X }
- X
- X return count;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Is the given uid trusted?
- X */
- X
- Xint
- Xtrusted_uid(uid)
- Xint uid;
- X{
- X CONTEXT *ct;
- X char **n;
- X static char *t[] = { TRUSTED_USERS, 0 };
- X
- X for (n = t; *n; ++n)
- X {
- X if ((ct = name_context(*n)) != NULL && uid == ct->ct_uid)
- X return TRUE;
- X }
- X
- X return FALSE;
- X}
- X
- X/*----------------------------------------------------------------------
- X * Set up useful environment variables.
- X */
- X
- Xsetup_environ()
- X{
- X char flags[8];
- X int f = 0;
- X
- X flags[f++] = '-';
- X if (verbose)
- X flags[f++] = (dryrun ? 'd' : 'v');
- X if (printaddrs)
- X flags[f++] = 'A';
- X if (leavetemps)
- X flags[f++] = 't';
- X flags[f] = 0;
- X
- X alloc_env(ENV_DFLAGS, (f > 1) ? flags : "");
- X if (sys_deliver && *sys_deliver)
- X alloc_env(ENV_SYSDEL, sys_deliver);
- X if (user_deliver && *user_deliver)
- X alloc_env(ENV_USERDEL, user_deliver);
- X if (hostname && *hostname)
- X alloc_env(ENV_HOSTNAME, hostname);
- X if (sender && *sender)
- X alloc_env(ENV_SENDER, sender);
- X
- X alloc_env("IFS", " \t\n");
- X del_env("ENV"); /* in case SHELL is ksh */
- X}
- END_OF_FILE
- if test 12025 -ne `wc -c <'main.c'`; then
- echo shar: \"'main.c'\" unpacked with wrong size!
- fi
- # end of 'main.c'
- fi
- echo shar: End of shell archive.
- exit 0
-
-
-